/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * cpuid.cc																	*
 *																			*
 * CPU Identification, and utilities for getting the information once it's	*
 * already gotten.															*
 *																			*
\***************************************************************************/

// includes
#include <system.h>
#include <console.h>
#include <memory.h>
#include <stdio.h>
#include <cpu.h>

// macros
#define dbg_print( s )	( cons.Output( s, true ) )

// data types

union stdlevl1_eax_t {
	uint_t eax;
	struct level1_CPUInfo1 data;
};

union stdlevl1_ebx_t {
	uint_t ebx;
	struct level1_CPUInfo2 data;
};

union stdlevl1_ecx_t {
	uint_t ecx;
	struct level1_FeatureSet1 data;
};

union stdlevl1_edx_t {
	uint_t edx;
	struct level1_FeatureSet2 data;
};

// functions

// detects if cpuid is supported
bool cpuid_support()
{
	// return value
	bool retval = false;
	
	// see if the ID bit in EFLAGS is set or not - let this get optomized
	asm(	"pushf\n"
			"pop %%eax\n"
			"movl %%eax,%%ecx\n" // save the current eflags
			"xor $0x00200000,%%eax\n" // id bit
			"push %%eax\n" // get ready to load new eflags
			"popf\n" // load into eflags
			"pushf\n" // load the newly changed eflags (will not have changed
						 //							if not supported)
			"pop %%eax\n"
			"cmp %%eax,%%ecx\n" // compare to the old version
			"jz 1f\n"
			"jmp 2f\n"
			"1:\n"
			"mov $0,%%eax\n"
			"jmp 3f\n"
			"2:\n"
			"mov $1,%%eax\n"
			"3:\n"
			:
			"=a" (retval)
		);
	
	// return whether or not it's supported
	return retval;
}

// calls cpuid
void cpuid( uint_t func, uint_t* eax, uint_t* ebx, uint_t* ecx, uint_t* edx )
{
	uint_t leax,lebx,lecx,ledx;
	asm( "cpuid" : "=a" (leax), "=b" (lebx), "=c" (lecx), "=d" (ledx) : "a" (func) );
	*eax = leax;
	*ebx = lebx;
	*ecx = lecx;
	*edx = ledx;
}

// global storage of the data
struct CPUID_Info cpuinfo;

// gets the cpu info
void GetCPUInfo( struct CPUID_Info* info )
{
	*info = cpuinfo;
}

// does detection
void CPUIdent()
{	
	// is cpuid supported?
	if( cpuid_support() )
	{
		// useful variables
		uint_t eax,ebx,ecx,edx;
		char VendorID[13];
		
		// clear the vendor id
		memset( VendorID, 0, 13 );
		
		// firstly get the actual type
		cpuid( 0, &eax, &ebx, &ecx, &edx );
		
		// and put into the vendor id
		*((uint_t*) &VendorID[0]) = ebx;
		*((uint_t*) &VendorID[4]) = edx;
		*((uint_t*) &VendorID[8]) = ecx;
		
		// print it
#ifdef DEBUG_CPUID
		kprintf( "A %s processor (supports up to %d standard%s)\n", VendorID, eax, eax == 1 ? "" : "s" );
#endif
		
		// check that we can execute standard level 1
		if( eax >= 1 )
		{
			// execute it
			cpuid( 1, &eax, &ebx, &ecx, &edx );
			
			// put in the stepping info
			union stdlevl1_eax_t stepping;
			stepping.eax = eax;
			cpuinfo.cpuinfo1 = stepping.data;
			
			// put in the brand info
			union stdlevl1_ebx_t brand;
			brand.ebx = ebx;
			cpuinfo.cpuinfo2 = brand.data;
			
			// put in the feature info
			union stdlevl1_ecx_t feature;
			feature.ecx = ecx;
			cpuinfo.features1 = feature.data;
			
			// put in the features (set 2)
			union stdlevl1_edx_t feature2;
			feature2.edx = edx;
			cpuinfo.features2 = feature2.data;
			
#ifdef DEBUG_CPUID
			// tell me info!
			kprintf( "Stepping: 0x%x\n", stepping.data.stepping );
			kprintf( "Model: 0x%x\n", stepping.data.model );
			kprintf( "Family: 0x%x\n", stepping.data.family );
			kprintf( "Type: 0x%x\n", stepping.data.type );
			kprintf( "Extended Model: 0x%x\n", stepping.data.extmodel );
			kprintf( "Extended Family: 0x%x\n", stepping.data.extfam );
			kprintf( "Brand: 0x%x\n", brand.data.brand );
			kprintf( "CLFLUSH: 0x%x\n", brand.data.clflush );
			kprintf( "Processor Count: 0x%x\n", brand.data.count );
			kprintf( "APIC ID: 0x%x\n", brand.data.apicid );
#endif
		}
	}
	else
	{
		// TODO: handle this
	}
}
